package com.ruoyi.web.controller.redis;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class RedisHashSetStorage<T> {
    private final RedisTemplate<String, Object> redisTemplate;
    private final ObjectMapper objectMapper;
    private final Class<T> clazz;
    private final RedisSerializer<String> stringSerializer;

    public RedisHashSetStorage(RedisTemplate<String, Object> redisTemplate, Class<T> clazz) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = new ObjectMapper();
        this.clazz = clazz;
        this.stringSerializer = redisTemplate.getStringSerializer();
    }

    /**
     * 批量存储数据到Redis
     *
     * @param keyPrefix    Redis键前缀
     * @param indexKey     索引集合键
     * @param dataList     数据列表
     * @param keyExtractor 键提取器
     * @param expireTime   过期时间（单位：秒）
     */
    public void batchStore(String keyPrefix, String indexKey, List<T> dataList,
                           Function<T, String> keyExtractor, long expireTime) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }

        // 使用管道批量操作
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (T data : dataList) {
                try {
                    String keySuffix = keyExtractor.apply(data);
                    String key = keyPrefix + ":" + keySuffix;
                    String dataJson = objectMapper.writeValueAsString(data);

                    // 存储对象到Hash
                    byte[] keyBytes = stringSerializer.serialize(key);
                    byte[] fieldBytes = stringSerializer.serialize("data");
                    byte[] valueBytes = stringSerializer.serialize(dataJson);

                    if (keyBytes != null && fieldBytes != null && valueBytes != null) {
                        connection.hSet(keyBytes, fieldBytes, valueBytes);

                        // 添加到索引集合
                        byte[] indexKeyBytes = stringSerializer.serialize(indexKey);
                        if (indexKeyBytes != null) {
                            connection.sAdd(indexKeyBytes, keyBytes);
                        }

                        // 设置过期时间
                        if (expireTime > 0) {
                            connection.expire(keyBytes, expireTime);
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException("序列化对象失败", e);
                }
            }

            // 设置索引集合的过期时间
            if (expireTime > 0) {
                byte[] indexKeyBytes = stringSerializer.serialize(indexKey);
                if (indexKeyBytes != null) {
                    connection.expire(indexKeyBytes, expireTime);
                }
            }
            return null;
        });
    }

    /**
     * 批量存储数据到Redis，并为指定字段创建二级索引
     *
     * @param keyPrefix      Redis键前缀
     * @param indexKey       主索引集合键
     * @param dataList       数据列表
     * @param keyExtractor   键提取器
     * @param fieldExtractor 二级索引字段提取器
     * @param secondaryIndex 二级索引前缀
     * @param expireTime     过期时间（单位：秒）
     */
    public void batchStoreWithSecondaryIndex(String keyPrefix, String indexKey, List<T> dataList,
                                             Function<T, String> keyExtractor,
                                             Function<T, String> fieldExtractor,
                                             String secondaryIndex, long expireTime) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }

        // 使用管道批量操作
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (T data : dataList) {
                try {
                    String keySuffix = keyExtractor.apply(data);
                    String key = keyPrefix + ":" + keySuffix;
                    String dataJson = objectMapper.writeValueAsString(data);
                    String fieldValue = fieldExtractor.apply(data);
                    String secondaryIndexKey = secondaryIndex + ":" + fieldValue;

                    // 存储对象到Hash
                    byte[] keyBytes = stringSerializer.serialize(key);
                    byte[] fieldBytes = stringSerializer.serialize("data");
                    byte[] valueBytes = stringSerializer.serialize(dataJson);

                    if (keyBytes != null && fieldBytes != null && valueBytes != null) {
                        connection.hSet(keyBytes, fieldBytes, valueBytes);

                        // 添加到主索引集合
                        byte[] indexKeyBytes = stringSerializer.serialize(indexKey);
                        if (indexKeyBytes != null) {
                            connection.sAdd(indexKeyBytes, keyBytes);
                        }

                        // 添加到二级索引集合
                        byte[] secondaryIndexKeyBytes = stringSerializer.serialize(secondaryIndexKey);
                        if (secondaryIndexKeyBytes != null) {
                            connection.sAdd(secondaryIndexKeyBytes, keyBytes);

                            // 设置二级索引集合的过期时间
                            if (expireTime > 0) {
                                connection.expire(secondaryIndexKeyBytes, expireTime);
                            }
                        }

                        // 设置过期时间
                        if (expireTime > 0) {
                            connection.expire(keyBytes, expireTime);
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException("序列化对象失败", e);
                }
            }

            // 设置主索引集合的过期时间
            if (expireTime > 0) {
                byte[] indexKeyBytes = stringSerializer.serialize(indexKey);
                if (indexKeyBytes != null) {
                    connection.expire(indexKeyBytes, expireTime);
                }
            }
            return null;
        });
    }

    /**
     * 分页查询数据
     *
     * @param indexKey   索引集合键
     * @param pageNum    页码
     * @param pageSize   每页大小
     * @param condition  查询条件
     * @return 分页结果
     */
    public PageResult<T> pageQuery(String indexKey, int pageNum, int pageSize, Predicate<T> condition) {
        return pageQuery(indexKey, pageNum, pageSize, condition, null);
    }

    /**
     * 分页查询数据（带排序）
     *
     * @param indexKey   索引集合键
     * @param pageNum    页码
     * @param pageSize   每页大小
     * @param condition  查询条件
     * @param comparator 排序比较器
     * @return 分页结果
     */
    public PageResult<T> pageQuery(String indexKey, int pageNum, int pageSize,
                                   Predicate<T> condition, Comparator<T> comparator) {
        if (pageNum < 1) pageNum = 1;
        if (pageSize < 1) pageSize = 10;

        long start = (pageNum - 1) * pageSize;

        try {
            Boolean member = redisTemplate.opsForSet().isMember("users:index", "user:1");
            Set<Object> members = redisTemplate.opsForSet().members(indexKey);
            // 获取所有键
            Set<String> keys = redisTemplate.opsForSet().members(indexKey)
                    .stream()
                    .map(obj -> (String) obj)
                    .collect(Collectors.toSet());

            if (CollectionUtils.isEmpty(keys)) {
                return new PageResult<>(Collections.emptyList(), 0, pageNum, pageSize);
            }
            List<Object> values = new ArrayList<>();
            for (String key : keys) {
                Object value = redisTemplate.opsForHash().get(key, "data");
                values.add(value);
            }
            // 转换并过滤数据
            List<T> allData = new ArrayList<>();
            for (Object value : values) {
                if (value != null) {
                    try {
                        T item = objectMapper.readValue(value.toString(), clazz);
                        if (condition == null || condition.test(item)) {
                            allData.add(item);
                        }
                    } catch (Exception e) {
                        // 忽略无法反序列化的数据
                    }
                }
            }

            // 排序
            if (comparator != null) {
                allData.sort(comparator);
            }

            // 手动分页
            List<T> resultList = allData.stream()
                    .skip(start)
                    .limit(pageSize)
                    .collect(Collectors.toList());

            // 获取总数
            long total = allData.size();

            return new PageResult<>(resultList, total, pageNum, pageSize);
        } catch (Exception e) {
            throw new RuntimeException("从Redis分页查询数据失败", e);
        }
    }

    /**
     * 根据二级索引分页查询数据
     *
     * @param secondaryIndexKey 二级索引键
     * @param pageNum           页码
     * @param pageSize          每页大小
     * @param condition         查询条件
     * @return 分页结果
     */
    public PageResult<T> pageQueryBySecondaryIndex(String secondaryIndexKey, int pageNum,
                                                   int pageSize, Predicate<T> condition) {
        return pageQuery(secondaryIndexKey, pageNum, pageSize, condition);
    }

    /**
     * 根据ID获取单个对象
     *
     * @param keyPrefix Redis键前缀
     * @param id        对象ID
     * @return 对象
     */
    public T getById(String keyPrefix, String id) {
        try {
            String key = keyPrefix + ":" + id;
            Object value = redisTemplate.opsForHash().get(key, "data");
            if (value != null) {
                return objectMapper.readValue(value.toString(), clazz);
            }
            return null;
        } catch (Exception e) {
            throw new RuntimeException("从Redis获取对象失败", e);
        }
    }

    /**
     * 批量获取对象
     *
     * @param keyPrefix Redis键前缀
     * @param ids       对象ID列表
     * @return 对象列表
     */
    public List<T> getByIds(String keyPrefix, List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }

        try {
            List<String> keys = ids.stream()
                    .map(id -> keyPrefix + ":" + id)
                    .collect(Collectors.toList());

            List<Object> values = redisTemplate.opsForHash().multiGet(keys.toString(), Collections.singletonList("data"));

            List<T> result = new ArrayList<>();
            for (Object value : values) {
                if (value != null) {
                    try {
                        T item = objectMapper.readValue(value.toString(), clazz);
                        result.add(item);
                    } catch (Exception e) {
                        // 忽略无法反序列化的数据
                    }
                }
            }

            return result;
        } catch (Exception e) {
            throw new RuntimeException("从Redis批量获取对象失败", e);
        }
    }

    /**
     * 删除对象
     *
     * @param keyPrefix Redis键前缀
     * @param indexKey  索引集合键
     * @param id        对象ID
     */
    public void delete(String keyPrefix, String indexKey, String id) {
        String key = keyPrefix + ":" + id;

        // 从索引集合中移除
        redisTemplate.opsForSet().remove(indexKey, key);

        // 删除对象
        redisTemplate.delete(key);
    }

    /**
     * 批量删除对象
     *
     * @param keyPrefix Redis键前缀
     * @param indexKey  索引集合键
     * @param ids       对象ID列表
     */
    public void batchDelete(String keyPrefix, String indexKey, List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }

        List<String> keys = ids.stream()
                .map(id -> keyPrefix + ":" + id)
                .collect(Collectors.toList());

        // 从索引集合中移除
        redisTemplate.opsForSet().remove(indexKey, keys.toArray());

        // 批量删除对象
        redisTemplate.delete(keys);
    }

    /**
     * 获取索引集合大小
     *
     * @param indexKey 索引集合键
     * @return 集合大小
     */
    public long getIndexSize(String indexKey) {
        Long size = redisTemplate.opsForSet().size(indexKey);
        return size != null ? size : 0;
    }

    /**
     * 分页结果类
     */
    public static class PageResult<T> {
        private List<T> list;
        private long total;
        private int pageNum;
        private int pageSize;
        private int pages;

        public PageResult(List<T> list, long total, int pageNum, int pageSize) {
            this.list = list;
            this.total = total;
            this.pageNum = pageNum;
            this.pageSize = pageSize;
            this.pages = (int) Math.ceil((double) total / pageSize);
        }

        // Getter方法
        public List<T> getList() { return list; }
        public long getTotal() { return total; }
        public int getPageNum() { return pageNum; }
        public int getPageSize() { return pageSize; }
        public int getPages() { return pages; }

        @Override
        public String toString() {
            return "PageResult{" +
                    "list=" + list +
                    ", total=" + total +
                    ", pageNum=" + pageNum +
                    ", pageSize=" + pageSize +
                    ", pages=" + pages +
                    '}';
        }
    }
}
